from bisect import bisect
from ftpext import ftpext 
from smbclient import SambaClient 
from time import strptime, mktime 
import re
import sys
import sqlite3

import os

'''
Crea las tablas necesarias para el scaneo de directorios
'''
def watchCreateTables(db = None):
    if db:
	try:
    	    con = sqlite3.connect(db)
    	    cur = con.cursor()
	    cur.executescript("""CREATE TABLE urls  (id         INTEGER PRIMARY KEY ASC,
                            	    url        TEXT UNIQUE,
                            	    last_enum  INTEGER,
                            	    last_error TEXT);
    
    
                                 CREATE TABLE files (id         INTEGER PRIMARY KEY ASC,
                            	    url_id	    INTEGER,
                            	    name	    TEXT,
                            	    size        INTEGER,
                            	    mode        INTEGER,
                            	    atime	    INTEGER,
                            	    ctime       INTEGER,
                            	    mtime       INTEGER);""")
    	    cur.close()
    	    con.commit()
    	    con.close()
    	    return True
    	except:
    	    return False
    	    
    return False





class WatchFile(object):
    def __init__(self, name = None, size = 0, mode = None, atime = None, ctime = None, mtime = None):
        self.name = name
        self.size = size
        self.mode = mode
        self.atime = atime
        self.ctime = ctime
        self.mtime = mtime

    def __eq__(self, other):
        if self.name == other.name:
	    return self.size == other.size and self.mtime == other.mtime  

	else:
	    return False
	return self.name == other.name

    def __gt__(self, other):
        return self.name >  other.name

    def __lt__(self, other):
        return self.name < other.name

    def __ge__(self, other):
        return self.name >= other.name

    def __le__(self, other):
        return self.name <= other.name

    def __ne__(self, other):
        return self.name != other.name





class WatchFolder(object):
    def __init__(self):
        self.db_con = None

    '''
    registerDataBase(): Crea el archivo de la base de datos
    '''
    def registerDataBase(self, db_file = None):

        if db_file:
            if not os.path.isfile(db_file):
                if not watchCreateTables(db_file):
                    return False
        
            try:	
                self.db_con = sqlite3.connect(db_file)
                self.db_con.text_factory = str
                return True
            except:
                return False
	
	return False
	

    '''
    watchDumpFileList(): Vuelca la lista de objetos WatchFile a la base de datos
    '''
    def watchDumpFileList(self, fileList = [], url = None):

	if fileList == [] or url == None:
            pass
        else:
	    url_id = self.watchInsertUrl(url)
            if url_id:

                cur = self.db_con.cursor()
	
		for data in lst:
		    cur.execute("INSERT INTO files (url_id, name, size, mode, atime, ctime, mtime) VALUES (?,?,?,?,?,?,?)", (url_id, data.name, data.size, data.mode, data.atime,data.ctime,data.mtime))

		self.db_con.commit()
            	cur.close()


    '''
    watchDeleteFilesByUrlId() Borra los archivos pertenecientes a un url_id
    '''

    def watchDeleteFilesByUrlId(self, url_id):
        if self.db_con:
            #
            # Elimina de la base de datos todos los files de una URL
            #
            cur = self.db_con.cursor()
            cur.execute("DELETE FROM files WHERE url_id=?", [url_id] )
            self.db_con.commit()
            cur.close()



    def watchDeleteFilesByUrl(self, url):
        return self.watchDeleteFilesByUrlId(self.watchGetUrlId(url))



    def watchGetUrlId(self, url = None):
        ret = None
        if self.db_con and None:
            #
            # Retorna el url_id o None
            #
            cur = self.db_con.cursor()
            cur.execute("SELECT id FROM urls WHERE url=?", [url])
            ret = cur.fetchone()
            cur.close()
        
        if ret:
    	    return ret[0]
    
        return ret


    def watchGetFilesByUrl(self, url = None):
        FileListResult = []
        if self.db_con and url:
            url_id = self.watchInsertUrl(url)

            if url_id:
                cur = self.db_con.cursor()
                cur.execute("SELECT name, size, mode, atime, ctime, mtime from files where url_id=?", [url_id])
                allFiles = cur.fetchall()
                for name, size, mode, atime, ctime, mtime in allFiles:

                    FileListResult.append(WatchFile(name, size, mode, atime, ctime, mtime))

                cur.close()

            return FileListResult

	return None
	

    def watchInsertUrl(self, url = None):
        if self.db_con and url:

            try: 
                cur = self.db_con.cursor()
                cur.execute("INSERT INTO urls (url, last_enum, last_error) VALUES (?, 0, '')", (url,))		
                self.db_con.commit()
                cur.close()
            except:
		pass

            return self.watchGetUrlId(url)

	return None

    def watchBinarySearch(self, lst, file):
	lo = 0
	hi = len(lst)

	return self.binary_search(lst,file, lo, hi) 


    def binary_search(self, a,x,lo=0,hi=-1):
        i = bisect(a,x,lo,hi)
        if i == 0:
            return False
        elif a[i-1] == x:
            return True
        else:
            return False



    def watchGetFtpFiles(self, host, user, passwd, path ):
        FileListResult = []
	ftp = ftpext(host)

	if ftp:
	    ftp.login(user, passwd)
	    ftp.cwd(path)
	    lst = ftp.nlst()
	    for file in lst:
		try:
		    size = ftp.size(file)
		    mdtm = ftp.mdtm(file)
		except:
		    size = 0
		    mdtm = 0
		FileListResult.append(watchFile(file, size, 0, 0, 0, mdtm))
	
	ftp.quit()
	ftp.close()

	return FileListResult
	
	

    def watchGetSmbFiles(self, host, user, passwd, share, path, onlyFiles = True ):
        FileListResult = []

	smb =  SambaClient(server=host, share=share, username=user, password=passwd)

	lst = smb.lsdir(path)
	for name, mode, size, date in lst:
            mtime = mktime(date.timetuple())
	    if onlyFiles:
		if smb.isfile(path + name):
		    FileListResult.append(WatchFile(name, size, mode, 0, 0, mtime))
	    else:
	        FileListResult.append(WatchFile(name, size, mode, 0, 0, mtime))

	return FileListResult


    
    def watchGetLocalFiles(self, path = None, onlyFiles = True ):
        FileListResult = []

	if path == None:
	    return FileListResult

	if not path.endswith('/'):
	    path = path + '/'       
 
        if os.path.exists(path):
            lst = os.listdir(path)
            for file in lst:
		if onlyFiles:
		    if os.path.isfile(path+file):
                        st = os.stat(path+file)			
                        FileListResult.append(WatchFile(file, st.st_size, st.st_mode, st.st_atime, st.st_ctime, st.st_mtime))
		else:
		    st = os.stat(path+file)
                    FileListResult.append(WatchFile(file, st.st_size, st.st_mode, st.st_atime, st.st_ctime, st.st_mtime))
	

        return FileListResult



    def watchMatch(self, name = None, include=None, exclude=None):

	if name == None:
	    return False

	if include:
	    if re.match(include, name):	
		#
		# Pasa por la lista blanca
		#
		if exclude:
		   if re.match(exclude, name):
			return False
		
		return True
	    else:
		return False

	if exclude:
		# 
		# Bloqueado por lista negra
		#
	    if re.match(exclude, name):
		return False

	return True	



    def watchUrlProto(self, url):
        result = re.match("(local|ftp|smb):(.+)", url)
        if result:
            return result.group(1)

        return None

    def watchUrlGetLocalPath(self, url):
	result = re.match("local:(.+)", url)
	return result.group(1)


    def PyWatchUrlGetFtpData(self, url):
	result = re.match("ftp://(.+):(.+)@(.+):(.+)", url)
	if result:
	    return result.group(1), result.group(2), result.group(3), result.group(4) 	
	
	return None


    def watchUrlGetSmbData(self, url):
	
	result = re.match("smb://(.+):(.+)@(.+):/(.+)", url)

	if result:
	    if result.group(4):
		data = result.group(4).split('/')
		if len(data) > 0:
		    path  = '' 
		    share = data[0]
		    for d in data[1:]:
			path = path + '/' + d

		    path = path + '/'
	
	            return result.group(1), result.group(2), result.group(3), share, path 


    def Scan(self, url, include=None, exclude=None, onlyFiles=False):
	result = []
	

	proto = self.watchUrlProto(url)
	if proto:
	    if proto == "local":
		path = self.watchUrlGetLocalPath(url)

		if path:
		    local  = self.watchGetLocalFiles(path, onlyFiles)

	    elif proto == "ftp":
		user, password, host, path = self.watchUrlGetFtpData(url)
		if user and password and host and path:
		    local = self.watchGetFtpFiles(host, user, password, path)

	    elif proto == "smb":
		user, password, host, share, path = self.watchUrlGetSmbData(url)
		if user and password and host and path and share:
		    local = self.watchGetSmbFiles(host, user, password, share, path, onlyFiles)



	    local.sort()

	    base   = self.watchGetFilesByUrl(url)

	    for data in local:
	        if self.watchBinarySearch(base, data):
         	    pass 
                else:
                    if self.watchMatch(data.name, include, exclude):
                        result.append(data)
	

	    self.watchDeleteFilesByUrl(url)
	    self.watchDump(local, url)

        return result


#watch = pyWatchFolder()
#watch.registerDb('./db')
#x = watch.Scan("smb://Administrator:Iplusb10@10.221.40.232:/rhozet/232_inbox/PROMOS_TO_TIBA", include='(.+).mov' )
#for i in x:
#    print i.name
